/**
 * Semaphore类的演示
 *
 * @author fireway
 * @since 2019年 03月 11日 星期一 07:12:18 CST
 */
package concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * A task to check a resource out of a pool
 */
class CheckoutTask<T> implements Runnable {
    private Pool<T> mPool;

    public CheckoutTask(Pool<T> pool) {
        mPool = pool;
    }

    @Override
    public void run() {
        try {
            T item = mPool.checkOut();
            System.out.println(this + " checked out " + item);
            TimeUnit.SECONDS.sleep(1);
            System.out.println(this + " checking in " + item);
            mPool.checkIn(item);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class SemaphoreDemo {
    final static int SIZE = 25;

    public static void main(String[] args) {
        final Pool<Fat> pool = new Pool<Fat>(Fat.class, SIZE);
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i = 0; i < SIZE; i++) {
            exec.execute(new CheckoutTask<Fat>(pool));
        }
        System.out.println("All CheckoutTasks created");

        List<Fat> list = new ArrayList<Fat>();
        try {
            for(int i = 0; i < SIZE; i++) {
                Fat f = pool.checkOut();
                System.out.println(i + ": main() thread checked out " + f);
                list.add(f);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Future<?> blocked = exec.submit(new Runnable() {
            @Override
            public void run() {
                // Semaphore prevents additional checkout,
                // so call is blocked:
                try {
                    System.out.println(Thread.currentThread() + " checkout block...");
                    pool.checkOut();
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread() + " checkOut() Interrupted");
                    e.printStackTrace();
                }
            }
        });

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        blocked.cancel(true); // Break out of blocked call
        System.out.println(Thread.currentThread() +  " checking in objects in " + list);

        for(Fat f : list) {
            pool.checkIn(f);
        }

        for(Fat f : list) {
            // Second checkIn ignored
            pool.checkIn(f);
        }

        exec.shutdown();
    }
}
